home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume13 / forms.el / part01 next >
Encoding:
Text File  |  1990-06-15  |  24.1 KB  |  787 lines

  1. Newsgroups: comp.sources.misc
  2. subject: v13i049: Emacs forms mode 1.1 - part 01 of 03
  3. from: jv@mh.nl (Johan Vromans)
  4. Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  5.  
  6. Posting-number: Volume 13, Issue 49
  7. Submitted-by: jv@mh.nl (Johan Vromans)
  8. Archive-name: forms.el/part01
  9.  
  10. This is the first public release of GNU Emacs 'forms-mode'.
  11.  
  12. This GNU Emacs major mode implements editing a structured file (i.e. a
  13. file with 'records' and 'fields' in it) using a forms. 
  14. It is fully documented in the source file 'forms.el' and in the
  15. texinfo file 'forms.ti'.
  16.  
  17. ---- Cut Here and unpack ----
  18. #!/bin/sh
  19. # This is a shell archive (shar 3.24)
  20. # made 06/10/1990 10:33 UTC by jv@squirrel
  21. # Source directory /u/jv/elisp/src/forms-mode
  22. #
  23. # existing files WILL be overwritten
  24. #
  25. # This is part 1 of a multipart archive                                    
  26. # do not concatenate these parts, unpack them in order with /bin/sh        
  27. #
  28. # This shar contains:
  29. # length  mode       name
  30. # ------ ---------- ------------------------------------------
  31. #    860 -rw-r--r-- README
  32. #  36688 -r--r--r-- forms.el
  33. #  21254 -r--r--r-- forms.ti
  34. #    436 -rw-r--r-- demo1
  35. #    475 -rw-r--r-- demo2
  36. #    476 -rw-r--r-- demo2.dat
  37. #
  38. if touch 2>&1 | fgrep '[-amc]' > /dev/null
  39.  then TOUCH=touch
  40.  else TOUCH=true
  41. fi
  42. if test -r shar3_seq_.tmp; then
  43.     echo "Must unpack archives in sequence!"
  44.     next=`cat shar3_seq_.tmp`; echo "Please unpack part $next next"
  45.     exit 1
  46. fi
  47. # ============= README ==============
  48. echo "x - extracting README (Text)"
  49. sed 's/^X//' << 'SHAR_EOF' > README &&
  50. XThis is the first public release GNU Emacs 'forms-mode'.
  51. X
  52. XThis GNU Emacs major mode implements editing a structured file (i.e. a
  53. Xfile with 'records' and 'fields' in it) using a forms. 
  54. XIt is fully documented in the source file 'forms.el' and in the
  55. Xtexinfo file 'forms.ti'.
  56. X
  57. XThis kit contains:
  58. X
  59. X    README        - this file
  60. X    MANIFEST    - list of files
  61. X    forms.el    - the lisp source
  62. X    forms.ti    - texinfo file
  63. X    demo1        - demo using /etc/passwd
  64. X    demo2        - demo using 'demo2.dat'
  65. X    demo2.dat    - data for demo2
  66. X
  67. XLoad the lisp source, and execute
  68. X
  69. X    forms-find-file demo1
  70. X
  71. Xto look at your password file in a unconventional (but read-only) way.
  72. X
  73. X    forms-find-file demo2
  74. X
  75. Xgives you something to clobber with data and multi-line fields.
  76. X
  77. XThis program has been donated to the Free Software Foundation to be
  78. Xpart of their GNU Emacs programming system.
  79. X
  80. XHave fun!
  81. X
  82. X    Johan Vromans    <jv@mh.nl>
  83. SHAR_EOF
  84. $TOUCH -am 0610120990 README &&
  85. chmod 0644 README ||
  86. echo "restore of README failed"
  87. set `wc -c README`;Wc_c=$1
  88. if test "$Wc_c" != "860"; then
  89.     echo original size 860, current size $Wc_c
  90. fi
  91. # ============= forms.el ==============
  92. echo "x - extracting forms.el (Text)"
  93. sed 's/^X//' << 'SHAR_EOF' > forms.el &&
  94. X;;; Forms Mode - A GNU Emacs Major Mode        ; @(#)@ forms    1.1.2
  95. X;;; Created 1989 - Johan Vromans <jv@mh.nl>
  96. X;;;
  97. X;;; This file is part of GNU Emacs.
  98. X
  99. X;;; GNU Emacs is distributed in the hope that it will be useful,
  100. X;;; but WITHOUT ANY WARRANTY.  No author or distributor
  101. X;;; accepts responsibility to anyone for the consequences of using it
  102. X;;; or for whether it serves any particular purpose or works at all,
  103. X;;; unless he says so in writing.  Refer to the GNU Emacs General Public
  104. X;;; License for full details.
  105. X
  106. X;;; Everyone is granted permission to copy, modify and redistribute
  107. X;;; GNU Emacs, but only under the conditions described in the
  108. X;;; GNU Emacs General Public License.   A copy of this license is
  109. X;;; supposed to have been given to you along with GNU Emacs so you
  110. X;;; can know your rights and responsibilities. 
  111. X;;; If you don't have this copy, write to the Free Software
  112. X;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  113. X;;;
  114. X
  115. X(provide 'forms-mode)            ; Version 1.1.2
  116. X
  117. X;;; Visit a file using a form.
  118. X;;;
  119. X;;; === Naming conventions
  120. X;;;
  121. X;;; The names of all variables and functions start with 'form-'.
  122. X;;; Names which start with 'form--' are intended for internal use, and
  123. X;;; should *NOT* be used from the outside.
  124. X;;;
  125. X;;; All variables are buffer-local, to enable multiple forms visits 
  126. X;;; simultaneously.
  127. X;;; Variable 'forms--mode-setup' is local to *ALL* buffers, for it 
  128. X;;; controls if forms-mode has been enabled in a buffer.
  129. X;;;
  130. X;;; === How it works ===
  131. X;;;
  132. X;;; Forms mode means visiting a data file which is supposed to consist
  133. X;;; of records each containing a number of fields. The records are
  134. X;;; separated by a newline, the fields are separated by a user-defined
  135. X;;; field separater (default: TAB).
  136. X;;; When shown, a record is transferred to an emacs buffer and
  137. X;;; presented using a user-defined form. One record is shown at a
  138. X;;; time.
  139. X;;;
  140. X;;; Forms mode is a composite mode. It involves two files, and two
  141. X;;; buffers.
  142. X;;; The first file, called the control file, defines the name of the
  143. X;;; data file and the forms format. This file buffer will be used to
  144. X;;; present the forms.
  145. X;;; The second file holds the actual data. The buffer of this file
  146. X;;; will be buried, for it is never accessed directly.
  147. X;;;
  148. X;;; Forms mode is invoked using "forms-find-file control-file".
  149. X;;; Alternativily forms-find-file-other-window can be used.
  150. X;;;
  151. X;;; You may also visit the control file, and switch to forms mode by hand
  152. X;;; with M-x forms-mode .
  153. X;;;
  154. X;;; Automatic mode switching is supported, so you may use "find-file"
  155. X;;; if you specify "-*- forms -*-" in the first line of the control file.
  156. X;;; 
  157. X;;; The control file is visited, evaluated using
  158. X;;; eval-current-buffer, and should set at least the following
  159. X;;; variables:
  160. X;;;
  161. X;;;    forms-file            [string] the name of the data file.
  162. X;;;
  163. X;;;    forms-number-of-fields        [integer]
  164. X;;;            The number of fields in each record.
  165. X;;;
  166. X;;;    forms-format-list           [list]   formatting instructions.
  167. X;;;
  168. X;;; The forms-format-list should be a list, each element containing
  169. X;;;
  170. X;;;  - either a string, e.g. "hello" (which is inserted \"as is\"),
  171. X;;;
  172. X;;;  - an integer, denoting a field number. The contents of the field
  173. X;;;    are inserted at this point.
  174. X;;;    The first field has number one.
  175. X;;;
  176. X;;; Optional variables which may be set in the control file:
  177. X;;;
  178. X;;;    forms-field-sep                [string, default TAB]
  179. X;;;            The field separator used to separate the
  180. X;;;            fields in the data file. It may be a string.
  181. X;;;
  182. X;;;    forms-read-only                [bool, default nil]
  183. X;;;            't' means that the data file is visited read-only.
  184. X;;;            If no write access to the data file is
  185. X;;;            possible, read-only mode is enforced. 
  186. X;;;
  187. X;;;    forms-multi-line            [string, default "^K"]
  188. X;;;            If non-null the records of the data file may
  189. X;;;            contain fields which span multiple lines in
  190. X;;;            the form.
  191. X;;;            This variable denoted the separator character
  192. X;;;            to be used for this purpose. Upon display, all
  193. X;;;            occurrencies of this character are translated
  194. X;;;            to newlines. Upon storage they are translated
  195. X;;;            back to the separator.
  196. X;;;
  197. X;;;    forms-forms-scroll            [bool, default t]
  198. X;;;            If non-nil: redefine scroll-up/down to perform
  199. X;;;            forms-next/prev-field if in forms mode.
  200. X;;;
  201. X;;;    forms-forms-jump            [bool, default t]
  202. X;;;            If non-nil: redefine beginning/end-of-buffer
  203. X;;;            to performs forms-first/last-field if in
  204. X;;;            forms mode.
  205. X;;;
  206. X;;; After evaluating the control file, its buffer is cleared and used
  207. X;;; for further processing.
  208. X;;; The data file (as designated by "forms-file") is visited in a buffer
  209. X;;; (forms--file-buffer) which will not normally be shown.
  210. X;;; Great malfunctioning may be expected if this file/buffer is modified
  211. X;;; outside of this package while it's being visited!
  212. X;;;
  213. X;;; A record from the data file is transferred from the data file,
  214. X;;; split into fields (into forms--the-record-list), and displayed using
  215. X;;; the specs in forms-format-list.
  216. X;;; A format routine 'forms--format' is build upon startup to format 
  217. X;;; the records.
  218. X;;;
  219. X;;; When a form is changed the record is updated as soon as this form
  220. X;;; is left. The contents of the form are parsed using forms-format-list,
  221. X;;; and the fields which are deduced from the form are modified. So,
  222. X;;; fields not shown on the forms retain their origional values.
  223. X;;; The newly formed record and replaces the contents of the
  224. X;;; old record in forms--file-buffer.
  225. X;;; A parse routine 'forms--parser' is build upon startup to parse
  226. X;;; the records.
  227. X;;;
  228. X;;; Two exit functions exist: forms-exit (which saves) and forms-exit-no-save
  229. X;;; (which doesn't). However, if forms-exit-no-save is executed and the file
  230. X;;; buffer has been modified, emacs will ask questions.
  231. X;;;
  232. X;;; Other functions are:
  233. X;;;
  234. X;;;    paging (forward, backward) by record
  235. X;;;    jumping (first, last, random number)
  236. X;;;    searching
  237. X;;;    creating and deleting records
  238. X;;;    reverting the form (NOT the file buffer)
  239. X;;;    switching edit <-> view mode v.v.
  240. X;;;    jumping from field to field
  241. X;;;
  242. X;;; As an documented side-effect: jumping to the last record in the
  243. X;;; file (using forms-last-record) will adjust forms--total-records if
  244. X;;; needed.
  245. X;;;
  246. X;;; Commands and keymaps:
  247. X;;;
  248. X;;; A local keymap 'forms-mode-map' is used in the forms buffer.
  249. X;;; As conventional, this map can be accessed with C-c prefix.
  250. X;;; In read-only mode, the C-c prefix must be omitted.
  251. X;;;
  252. X;;; Default bindings:
  253. X;;;
  254. X;;;    \C-c    forms-mode-map
  255. X;;;    TAB    forms-next-field
  256. X;;;    SPC     forms-next-record
  257. X;;;    <    forms-first-record
  258. X;;;    >    forms-last-record
  259. X;;;    ?    describe-mode
  260. X;;;    d    forms-delete-record
  261. X;;;    e    forms-edit-mode
  262. X;;;    i    forms-insert-record
  263. X;;;    j    forms-jump-record
  264. X;;;    n    forms-next-record
  265. X;;;    p    forms-prev-record
  266. X;;;    q    forms-exit
  267. X;;;    s    forms-search
  268. X;;;    v    forms-view-mode
  269. X;;;    x    forms-exit-no-save
  270. X;;;    DEL    forms-prev-record
  271. X;;;
  272. X;;; Standard functions scroll-up, scroll-down, beginning-of-buffer and
  273. X;;; end-of-buffer are wrapped with re-definitions, which map them to
  274. X;;; next/prev record and first/last record.
  275. X;;; Buffer-local variables forms-forms-scroll and forms-forms-jump
  276. X;;; may be used to control these redefinitions.
  277. X;;;
  278. X;;; Function save-buffer is also wrapped to perform a sensible action.
  279. X;;; A revert-file-hook is defined to revert a forms to original.
  280. X;;;
  281. X;;; For convenience, TAB is always bound to forms-next-field, so you
  282. X;;; don't need the C-c prefix for this command.
  283. X;;;
  284. X;;; Global variables and constants
  285. X
  286. X(defconst forms-version "1.1.2"
  287. X  "Version of forms-mode implementation")
  288. X
  289. X(defvar forms-forms-scrolls t
  290. X  "If non-null: redefine scroll-up/down to be used with forms-mode.")
  291. X
  292. X(defvar forms-forms-jumps t
  293. X  "If non-null: redefine beginning/end-of-buffer to be used with forms-mode.")
  294. X
  295. X(defvar forms-mode-hooks nil
  296. X  "Hook functions to be run upon entering forms mode.")
  297. X;;;
  298. X;;; Mandatory variables - must be set by evaluating the control file
  299. X
  300. X(defvar forms-file nil
  301. X   "Name of the file holding the data.")
  302. X
  303. X(defvar forms-format-list nil
  304. X  "Formatting specifications:
  305. X
  306. XIt should be a list, each element containing 
  307. X
  308. X - either a string, e.g. "hello" (which is inserted \"as is\"),
  309. X
  310. X - an integer, denoting the number of a field which contents are
  311. X   inserted at this point.
  312. X   The first field has number one.
  313. X")
  314. X
  315. X(defvar forms-number-of-fields nil
  316. X  "Number of fields per record.")
  317. X
  318. X;;;
  319. X;;; Optional variables with default values
  320. X
  321. X(defvar forms-field-sep "\t"
  322. X  "Field separator character (default TAB)")
  323. X
  324. X(defvar forms-read-only nil
  325. X  "Read-only mode (defaults to the write access on the data file).")
  326. X
  327. X(defvar forms-multi-line "\C-k"
  328. X  "Character to separate multi-line fields (default ^K)")
  329. X
  330. X(defvar forms-forms-scroll t
  331. X  "Redefine scroll-up/down to perform forms-next/prev-record when in
  332. X forms mode.")
  333. X
  334. X(defvar forms-forms-jump t
  335. X  "Redefine beginning/end-of-buffer to perform forms-first/last-record
  336. X when in forms mode.")
  337. X
  338. X;;;
  339. X;;; Internal variables.
  340. X
  341. X(defvar forms--file-buffer nil
  342. X  "Buffer which holds the file data")
  343. X
  344. X(defvar forms--total-records 0
  345. X  "Total number of records in the data file.")
  346. X
  347. X(defvar forms--current-record 0
  348. X  "Number of the record currently on the screen.")
  349. X
  350. X(defvar forms-mode-map nil        ; yes - this one is global
  351. X   "Keymap for form buffer.")
  352. X
  353. X(defvar forms--markers nil
  354. X  "Field markers in the screen.")
  355. X
  356. X(defvar forms--number-of-markers 0
  357. X  "Number of fields on screen.")
  358. X
  359. X(defvar forms--the-record-list nil 
  360. X   "List of strings of the current record, as parsed from the file.")
  361. X
  362. X(defvar forms--search-regexp nil
  363. X  "Last regexp used by forms-search.")
  364. X
  365. X(defvar forms--format nil
  366. X  "Formatting routine.")
  367. X
  368. X(defvar forms--parser nil
  369. X  "Forms parser routine.")
  370. X
  371. X(defvar forms--mode-setup nil
  372. X  "*Internal* - keeps track of forms-mode being set-up.")
  373. X(make-variable-buffer-local 'forms--mode-setup)
  374. X
  375. X;;;
  376. X;;; forms-mode
  377. X;;;
  378. X;;; This is not a simple major mode, as usual. Therefore, forms-mode
  379. X;;; takes an optional argument 'primary' which is used for the initial
  380. X;;; set-up. Normal use would leave 'primary' to nil.
  381. X;;;
  382. X;;; A global buffer-local variable 'forms--mode-setup' has the same effect
  383. X;;; but makes it possible to auto-invoke forms-mode using find-file.
  384. X;;;
  385. X;;; Note: although it seems logical to have (make-local-variable) executed
  386. X;;; where the variable is first needed, I deliberately placed all calls
  387. X;;; in the forms-mode function.
  388. X(defun forms-mode (&optional primary)
  389. X  "Major mode to visit files in a field-structured manner using a form.
  390. X
  391. X Commands (prefix with C-c if not in read-only mode):
  392. X \\{forms-mode-map}"
  393. X
  394. X  (interactive)                ; no - 'primary' is not prefix arg
  395. X
  396. X  ;; Primary set-up: evaluate buffer and check if the mandatory
  397. X  ;; variables have been set.
  398. X  (if (or primary (not forms--mode-setup))
  399. X      (progn
  400. X    (kill-all-local-variables)
  401. X
  402. X    ;; make mandatory variables
  403. X    (make-local-variable 'forms-file)
  404. X    (make-local-variable 'forms-number-of-fields)
  405. X    (make-local-variable 'forms-format-list)
  406. X
  407. X    ;; make optional variables
  408. X    (make-local-variable 'forms-field-sep)
  409. X        (make-local-variable 'forms-read-only)
  410. X        (make-local-variable 'forms-multi-line)
  411. X    (make-local-variable 'forms-forms-scroll)
  412. X    (make-local-variable 'forms-forms-jump)
  413. X
  414. X    ;; eval the buffer, should set variables
  415. X    (eval-current-buffer)
  416. X
  417. X    ;; check if the mandatory variables make sense.
  418. X    (or forms-file
  419. X        (error "'forms-file' has not been set"))
  420. X    (or forms-number-of-fields
  421. X        (error "'forms-number-of-fields' has not been set"))
  422. X    (or (> forms-number-of-fields 0)
  423. X        (error "'forms-number-of-fields' must be > 0")
  424. X    (or (stringp forms-field-sep))
  425. X        (error "'forms-field-sep' is not a string"))
  426. X    (if forms-multi-line
  427. X        (if (and (stringp forms-multi-line)
  428. X             (eq (length forms-multi-line) 1))
  429. X        (if (string= forms-multi-line forms-field-sep)
  430. X            (error "'forms-multi-line' is equal to 'forms-field-sep'"))
  431. X          (error "'forms-multi-line' must be nil or a one-character string")))
  432. X        
  433. X    ;; validate and process forms-format-list
  434. X    (make-local-variable 'forms--number-of-markers)
  435. X    (make-local-variable 'forms--markers)
  436. X    (forms--process-format-list)
  437. X
  438. X    ;; build the formatter and parser
  439. X    (make-local-variable 'forms--format)
  440. X    (forms--make-format)
  441. X    (make-local-variable 'forms--parser)
  442. X    (forms--make-parser)
  443. X
  444. X    ;; prepare this buffer for further processing
  445. X    (setq buffer-read-only nil)
  446. X
  447. X    ;; prevent accidental overwrite of the control file
  448. X    (setq buffer-file-name nil)
  449. X
  450. X    ;; and clean it
  451. X    (erase-buffer)))
  452. X
  453. X  ;; make local variables
  454. X  (make-local-variable 'forms--file-buffer)
  455. X  (make-local-variable 'forms--total-records)
  456. X  (make-local-variable 'forms--current-record)
  457. X  (make-local-variable 'forms--the-record-list)
  458. X  (make-local-variable 'forms--search-rexexp)
  459. X
  460. X  ;; A bug in the current Emacs release 18.54 prevents a keymap
  461. X  ;; which is buffer-local from being used by 'describe-mode'.
  462. X  ;; Hence we'll leave it global.
  463. X  ;;(make-local-variable 'forms-mode-map)
  464. X  (if forms-mode-map            ; already defined
  465. X      nil
  466. X    (setq forms-mode-map (make-keymap))
  467. X    (forms--mode-commands forms-mode-map)
  468. X    (forms--change-commands))
  469. X
  470. X  ;; find the data file
  471. X  (setq forms--file-buffer (find-file-noselect forms-file))
  472. X
  473. X  ;; count the number of records, and set see if it may be modified
  474. X  (let (ro)
  475. X    (setq forms--total-records
  476. X      (save-excursion
  477. X        (set-buffer forms--file-buffer)
  478. X        (bury-buffer (current-buffer))
  479. X        (setq ro buffer-read-only)
  480. X        (count-lines (point-min) (point-max))))
  481. X    (if ro
  482. X    (setq forms-read-only t)))
  483. X
  484. X  ;; set the major mode indicator
  485. X  (setq major-mode 'forms-mode)
  486. X  (setq mode-name "Forms")
  487. X  (make-local-variable 'minor-mode-alist) ; needed?
  488. X  (forms--set-minor-mode)
  489. X  (forms--set-keymaps)
  490. X
  491. X  (set-buffer-modified-p nil)
  492. X
  493. X  ;; We have our own revert function - use it
  494. X  (make-local-variable 'revert-buffer-function)
  495. X  (setq revert-buffer-function 'forms-revert-buffer)
  496. X
  497. X  ;; setup the first (or current) record to show
  498. X  (if (< forms--current-record 1)
  499. X      (setq forms--current-record 1))
  500. X  (forms-jump-record forms--current-record)
  501. X
  502. X  ;; be helpful
  503. X  (forms--help)
  504. X
  505. X  ;; just in case
  506. X  (run-hooks 'forms-mode-hooks)
  507. X
  508. X  ;; initialization done
  509. X  (setq forms--mode-setup t))
  510. X
  511. X;;;
  512. X;;; forms-process-format-list
  513. X;;;
  514. X;;; Validates forms-format-list.
  515. X;;;
  516. X;;; Sets forms--number-of-markers and forms--markers.
  517. X
  518. X(defun forms--process-format-list ()
  519. X  "Validate forms-format-list and set some global variables."
  520. X
  521. X  ;; it must be non-nil
  522. X  (or forms-format-list
  523. X      (error "'forms-format-list' has not been set"))
  524. X  ;; it must be a list ...
  525. X  (or (listp forms-format-list)
  526. X      (error "'forms-format-list' is not a list"))
  527. X
  528. X  (setq forms--number-of-markers 0)
  529. X
  530. X  (let ((the-list forms-format-list)    ; the list of format elements
  531. X    (field-num 0))            ; highest field number 
  532. X
  533. X    (while the-list
  534. X
  535. X      (let ((el (car-safe the-list))
  536. X        (rem (cdr-safe the-list)))
  537. X
  538. X    (cond
  539. X
  540. X     ;; try string ...
  541. X     ((stringp el))            ; string is OK
  542. X      
  543. X     ;; try int ...
  544. X     ((numberp el)            ; check it
  545. X
  546. X      (if (or (<= el 0)
  547. X          (> el forms-number-of-fields))
  548. X          (error
  549. X           "forms error: field number %d out of range 1..%d"
  550. X           el forms-number-of-fields))
  551. X
  552. X      (setq forms--number-of-markers (1+ forms--number-of-markers))
  553. X      (if (> el field-num)
  554. X          (setq field-num el)))
  555. X
  556. X     ;; else
  557. X     (t
  558. X      (error "invalid element in 'forms-format-list': %s"
  559. X         (prin1-to-string el)))
  560. X
  561. X     ;; dead code - we'll need it in the future
  562. X     ((consp el)            ; check it
  563. X
  564. X      (let ((str (car-safe el))
  565. X        (idx (cdr-safe el)))
  566. X
  567. X        (cond
  568. X
  569. X         ;; car must be string
  570. X         ((not (stringp str))
  571. X          (error "forms error: car of cons %s must be string"
  572. X             (prin1-to-string el)))
  573. X
  574. X         ;; cdr must be number, > zero
  575. X         ((or (not (numberp idx))
  576. X          (<= idx 0)
  577. X          (> idx forms-number-of-fields))
  578. X          (error
  579. X           "forms error: cdr of cons %s must be a number between 1 and %d"
  580. X           (prin1-to-string el)
  581. X           forms-number-of-fields)))
  582. X
  583. X        ;; passed the test - handle it
  584. X        (setq forms--number-of-markers (1+ forms--number-of-markers))
  585. X        (if (> idx field-num)
  586. X        (setq field-num idx)))))
  587. X
  588. X    ;; advance to next element of the list
  589. X    (setq the-list rem))))
  590. X
  591. X  (setq forms--markers (make-vector forms--number-of-markers nil)))
  592. X
  593. X
  594. X;;;
  595. X;;; Build the format routine from forms-format-list.
  596. X;;;
  597. X;;; The format routine (forms--format) will look like
  598. X;;; 
  599. X;;; (lambda (arg)
  600. X;;;
  601. X;;;   ;;  "text: "
  602. X;;;   (insert "text: ")
  603. X;;;   ;;  6
  604. X;;;   (aset forms--markers 0 (point-marker))
  605. X;;;   (insert (elt arg 5))
  606. X;;;   ;;  "\nmore text: "
  607. X;;;   (insert "\nmore text: ")
  608. X;;;   ;;  9
  609. X;;;   (aset forms--markers 1 (point-marker))
  610. X;;;   (insert (elt arg 8))
  611. X;;;
  612. X;;;   ... )
  613. X;;; 
  614. X
  615. X(defun forms--make-format ()
  616. X  "Parse forms-format-list and build forms--format function"
  617. X  (setq forms--format nil)
  618. X
  619. X  (let ((the-list forms-format-list)    ; the list of format elements
  620. X    (the-result nil)        ; the strings and elements
  621. X    (pending-text nil)        ; accumulated text
  622. X    (the-marker 0))            ; number of current marker
  623. X
  624. X    (while the-list
  625. X
  626. X      (let ((el (car-safe the-list))
  627. X        (rem (cdr-safe the-list)))
  628. X
  629. X    (cond
  630. X
  631. X     ((stringp el)        ; element is a string
  632. X
  633. X      (setq el (prin1-to-string el)) ; quote it
  634. X
  635. X      (if (stringp pending-text)    ; text is pending ...
  636. X          (setq pending-text    ; concatenate it
  637. X            (concat (substring pending-text 0 -1) 
  638. X                (substring el 1)))
  639. X        (setq pending-text el)))    ; else set it
  640. X      
  641. X     ;; else ...
  642. X     ((numberp el)            ; number -> field id
  643. X
  644. X      (if (stringp pending-text)    ; text pending
  645. X          (setq the-result
  646. X            (concat the-result "(insert " pending-text ") ")))
  647. X      (setq pending-text nil)
  648. X
  649. X      (setq the-result
  650. X        (concat the-result "(aset forms--markers "
  651. X            the-marker " (point-marker)) "
  652. X            "(insert (elt arg " (1- el) ")) "))
  653. X      (setq the-marker (1+ the-marker))))
  654. X
  655. X    ;; advance to next element of the list
  656. X    (setq the-list rem)))
  657. X
  658. X    (if (stringp pending-text)    ; text pending
  659. X    (setq the-result
  660. X          (concat the-result "(insert " pending-text ") ")))
  661. X
  662. X    ;; use the lisp reader to evalute the string to a function
  663. X    (setq the-result (concat "(lambda (arg) " the-result ")"))
  664. X    (let ((res (read-from-string the-result))
  665. X      (len  (length the-result)))
  666. X      ;; has the whole string been parsed?
  667. X      (if (= (cdr res) len)
  668. X      (setq forms--format (car res))
  669. X    ;; pity
  670. X    (error "forms--make-format failed at %d [of %d]" (cdr res) len)))))
  671. X;;;
  672. X;;; forms--make-parser.
  673. X;;;
  674. X;;; Generate parse routine from forms-format-list.
  675. X;;;
  676. X;;; The parse routine (forms--parser) will look like (give or take
  677. X;;; a few " " .
  678. X;;; 
  679. X;;; (lambda nil
  680. X;;;   (let (here)
  681. X;;;     (goto-char (point-min))
  682. X;;; 
  683. X;;;    ;;  "text: "
  684. X;;;     (if (not (looking-at "text: "))
  685. X;;;         (error "parse error: cannot find \"text: \""))
  686. X;;;     (forward-char 6)    ; past "text: "
  687. X;;; 
  688. X;;;     ;;  6
  689. X;;;    ;;  "\nmore text: "
  690. X;;;     (setq here (point))
  691. X;;;     (if (not (search-forward "\nmore text: " nil t nil))
  692. X;;;         (error "parse error: cannot find \"\\nmore text: \""))
  693. X;;;     (aset the-recordv 5 (buffer-substring here (- (point) 12)))
  694. X;;;     ...
  695. X;;;     ... 
  696. X;;;     ;; final flush
  697. X;;;    (aset the-recordv 7 (buffer-substring (point) (point-max)))
  698. X;;; 
  699. X
  700. X(defun forms--make-parser ()
  701. X  "Generate parser function for forms."
  702. X
  703. X  (setq forms--parser nil)
  704. X
  705. X  (let ((the-list)            ; the list of format elements
  706. X    (the-result nil)        ; emerging function
  707. X    (pending-field nil)        ; pending element number
  708. X    (pending-text nil)        ; previous string element
  709. X    (pending-length 0))        ; length of pending string
  710. X
  711. X    ;; force flush of terminal string arguments
  712. X    (setq the-list (append forms-format-list '(0)))
  713. X
  714. X    (setq the-result "(lambda () (let (here) (goto-char (point-min)) ")
  715. X
  716. X    (while the-list
  717. X
  718. X      (let ((el (car-safe the-list))
  719. X        (rem (cdr-safe the-list)))
  720. X
  721. X    (cond
  722. X     
  723. X     ((stringp el)            ; element is a string
  724. X      
  725. X      (if (stringp pending-text)    ; text pending
  726. X          (progn
  727. X        (setq pending-length (+ pending-length (length el)))
  728. X        (setq pending-text
  729. X              (concat (substring pending-text 0 -1)
  730. X                  (substring (prin1-to-string 
  731. X                      (regexp-quote el)) 1))))
  732. X        (setq pending-length (length el))
  733. X        (setq pending-text (prin1-to-string (regexp-quote el)))))
  734. X      
  735. X     ;; else ...
  736. X     ((numberp el)            ; it's a field id
  737. X
  738. X      (if (and pending-field
  739. X           (null pending-text)
  740. X           (> el 0))
  741. X          (setq the-result
  742. X            (concat the-result
  743. X                "(error \"parse error: "
  744. X                "cannot parse adjacent fields "
  745. X                pending-field
  746. X                " and "
  747. X                el
  748. X                "\") ")))
  749. X
  750. X      (if (stringp pending-text)    ; text pending
  751. X
  752. X          (if (null pending-field)
  753. X                    ; simple string match
  754. X          (setq the-result
  755. X            (concat the-result
  756. X                "(if (not (looking-at " pending-text
  757. X                ")) (error \"parse error: not looking at \\\""
  758. X                (substring (prin1-to-string pending-text) 1 -1)
  759. X                "\\\"\")) (forward-char "
  760. X                pending-length
  761. X                ") "))
  762. X
  763. X        ;; else match using regexp and assign field
  764. X        (setq the-result
  765. X              (concat the-result
  766. X                  "(setq here (point)) "
  767. X                  "(if (not (search-forward " 
  768. X                  pending-text
  769. X                  " nil t nil))"
  770. X                  "(error \"parse error: cannot find \\\""
  771. X                  (substring (prin1-to-string pending-text) 1 -1)
  772. X                  "\\\"\")) (aset the-recordv "
  773. X                  (1- pending-field)
  774. X                  " (buffer-substring here (- (point) "
  775. X                  pending-length
  776. X                  "))) ")))
  777. X
  778. X        ;; else - no text, maybe a field?
  779. X        (if pending-field
  780. SHAR_EOF
  781. echo "End of  part 1"
  782. echo "File forms.el is continued in part 2"
  783. echo "2" > shar3_seq_.tmp
  784. exit 0
  785.  
  786.